Skip to content

release: dev → main (펫 뽑기 UX 개편, Dialog 재설계, 로그인 복구) - #399

Merged
sumi-0011 merged 4 commits into
mainfrom
dev
Jul 23, 2026
Merged

release: dev → main (펫 뽑기 UX 개편, Dialog 재설계, 로그인 복구)#399
sumi-0011 merged 4 commits into
mainfrom
dev

Conversation

@sumi-0011

@sumi-0011 sumi-0011 commented Jul 18, 2026

Copy link
Copy Markdown
Member

개요

dev 브랜치에 머지된 4개 작업을 main으로 배포합니다.

주요 변경사항

🥚 상점 펫 뽑기 UX 개편

💬 Dialog 시스템 재설계 (#398)

  • Dialog 코어를 5-size recipe로 재설계
  • Alert / Confirm sugar API 도입, 기존 CommonDialog·ConfirmDialog 정리
  • Storybook 스토리 추가

🔐 로그인 복구 UX 개선 (#387)

  • 로그인 끊김/실패 시 복구 UX 추가 (SessionExpiredDialog, LoginCallbackWatcher)
  • middleware.ts 세션 만료 처리 보강, sessionExpired 유틸 추가

변경 규모

46 files changed, +1738 / -1101

Summary by CodeRabbit

  • 새 기능

    • 펫 뽑기에서 알 부화와 10장 카드팩 공개 애니메이션을 제공합니다.
    • 희귀 펫 획득 시 시각 효과와 컨페티를 표시합니다.
    • 데스크톱 인증 연동, 인증 오류 안내 및 재시도 기능을 추가했습니다.
    • 세션 만료 시 로그인 안내를 표시하고 기존 경로로 복귀할 수 있습니다.
  • 개선

    • 모바일·터치 환경의 조작 안내와 한국어·영어 인증 문구를 보강했습니다.
    • 퀴즈 및 주요 화면의 확인·알림 대화상자 사용성을 개선했습니다.

sumi-0011 and others added 4 commits July 10, 2026 09:39
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 18, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

인증 관련 세션 만료/데스크톱 로그인 플로우가 새로 구성되고, interceptor와 middleware가 토큰 기반 처리로 변경되었다. Dialog 컴포넌트가 size 컨텍스트와 Alert/Confirm sugar API로 개편되어 다수 화면에 적용되었다. 가챠 카드팩/부화 애니메이션(CardPackGame, GachaHatchGame)이 신규 도입되어 OnePet/TenPet과 기존 카드 게임(TenCardFlipGame, FanDrawingGame)을 대체했다.

Changes

Dialog 시스템 개편 및 마이그레이션

Layer / File(s) Summary
Dialog 코어: size 컨텍스트, Alert, Confirm 추가
packages/ui/tailwind/src/components/dialog/Dialog.tsx, Alert.tsx, Confirm.tsx, Dialog.stories.tsx, index.ts
DialogSize 컨텍스트(sm/md/lg/screen/hero)와 DialogAlert/DialogConfirm sugar 컴포넌트를 추가하고 스토리로 사용례를 문서화함.
ConfirmDialog/CommonDialog 제거 및 화면 전환
SelectQuizType.tsx, QuizCreateForm.tsx, SolvingQuizSection.tsx, EvolutionPersona.tsx, MergePersona.tsx, useDialog.tsx, ConfirmDialog/*, CommonDialog.tsx
기존 ConfirmDialog/CommonDialog를 제거하고 퀴즈/진화/머지/전역 다이얼로그 화면을 Dialog.Confirm/Dialog.Alert 기반으로 교체함.
Dialog.Content size prop 값 단순화
FarmPersonaSelect.tsx, LinePersonaSelect.tsx, PersonaSearch.tsx, SelectedPetTable.tsx, EditModal.tsx, RouteModal.tsx
size="large"size="lg"로 통일하고 불필요한 커스텀 스타일 클래스를 제거함.

Estimated code review effort: 4 (Complex) | ~60 minutes

인증/세션 만료 및 데스크톱 로그인 플로우

Layer / File(s) Summary
세션 만료 전역 상태 및 유틸
utils/sessionExpired.ts
sessionExpiredAtomtriggerSessionExpired/resetSessionExpired 함수를 신규 정의하고 인증 경로 무시 로직을 포함함.
401 인터셉터 처리 변경
apis/interceptor.ts
401 응답 시 signOut() 대신 clearSessionCache()와 클라이언트 triggerSessionExpired()를 호출하도록 변경함.
세션 만료 다이얼로그 및 전역 컴포넌트 연결
Global/SessionExpiredDialog.tsx, Global/LoginCallbackWatcher.tsx, Global/GlobalComponent.tsx, AuthButton.tsx
세션 만료 시 로그인 유도 다이얼로그와 콜백 URL 감지/저장 컴포넌트를 추가하고 login()이 저장된 콜백 URL을 사용하도록 변경함.
미들웨어 인증 리다이렉트 방식 변경
middleware.ts
withAuth 대신 getToken으로 직접 판정하며, 토큰이 없을 때 로케일 리다이렉트 및 callbackUrl 보존을 구현함.
데스크톱 인증/에러 페이지
auth/desktop/page.tsx, auth/error/page.tsx
번역 키 기반 UI와 버튼 클릭 로그인으로 개편하고, 신규 에러 페이지에서 에러 코드별 안내 및 재시도/홈 이동을 구현함.
Auth/Gotcha 번역 키 추가
messages/en-US.json, messages/ko-KR.json
Auth 네임스페이스와 Gotcha 모바일 안내 문구 키를 추가함.

Estimated code review effort: 3 (Moderate) | ~40 minutes

가챠 카드팩/부화 게임 신규 구현

Layer / File(s) Summary
기존 카드 게임 제거
TenCardFlipGame.tsx, FanDrawingGame/FanDrawingGame.tsx, FanDrawingGame/CardMotion.tsx, laboratory/AlertDialog.tsx
카드 드로잉/플립 애니메이션과 그 헬퍼 컴포넌트를 삭제함.
CardPackGame: 10연차 개봉 애니메이션
shop/_petGotcha/CardPackGame.tsx
덱 클릭→순차 플립→희귀 강조 컨페티→파이널 최상위 카드 표시 단계별 애니메이션과 스킵 처리를 구현함.
GachaHatchGame: 1연차 부화 애니메이션
shop/_petGotcha/GachaHatchGame.tsx
티어별 테마와 흔들기/컨페티가 적용된 알 부화 시퀀스 및 결과 카드 표시를 구현함.
OnePet/TenPet 화면 연동
shop/_petGotcha/OnePet.tsx, TenPet.tsx
busy 상태로 다이얼로그 닫힘을 제어하며 새 게임 컴포넌트를 연결하고, 토큰 만료/슬랙 알림 등 에러 처리를 확장함.
의존성 추가
apps/web/package.json
canvas-confetti 및 타입 정의 의존성을 추가함.

Estimated code review effort: 4 (Complex) | ~55 minutes

Possibly related PRs

Suggested labels: area: API, area: Utils

Suggested reviewers: hyesungoh, orchemi

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 22.73% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 주요 변경인 펫 뽑기 UX 개편, Dialog 재설계, 로그인 복구를 잘 요약한 제목입니다.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dev

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
apps/web/src/app/[locale]/shop/_petGotcha/TenPet.tsx (1)

61-68: 🔒 Security & Privacy | 🔴 Critical | ⚡ Quick win

Slack 오류 메시지에서 access token을 제거하세요.

Line 67은 세션 access token을 외부 채널에 그대로 노출합니다. 또한 JSON.stringify(error)에도 요청 헤더가 포함될 수 있으므로 허용된 오류 필드만 전송해야 합니다.

🔒 제안 수정
-Error Message: ${JSON.stringify(error)}
+Error Message: ${error instanceof Error ? error.message : 'Unknown error'}
 \`\`\`
 User: ${data?.user.name}
-Token: ${data?.user.accessToken}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/src/app/`[locale]/shop/_petGotcha/TenPet.tsx around lines 61 - 68,
Update the error notification in the pet-draw failure handler around
sendMessageToErrorChannel to remove data?.user.accessToken entirely and avoid
serializing the full error object; include only explicitly allowed,
non-sensitive error fields in the Slack message.
packages/ui/tailwind/src/components/dialog/Dialog.tsx (1)

72-95: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

scrollablescreen/hero의 풀뷰포트 높이를 덮어쓰지 않도록 분기하세요.

Line 94에서 scrollable을 사용하면 h-screen 뒤에 h-[calc(100vh-40px)]가 병합되어, 여백 없는 screen/hero 계약이 깨집니다.

수정 예시
-const dialogScrollableStyle = 'h-[calc(100vh-40px)] overflow-hidden [&_.dialog-title]:shrink-0';
+const dialogScrollableStyle = 'overflow-hidden [&_.dialog-title]:shrink-0';
...
 className={cn(
   dialogContentCva({ size: resolvedSize }),
   scrollable && dialogScrollableStyle,
+  scrollable &&
+    resolvedSize !== 'screen' &&
+    resolvedSize !== 'hero' &&
+    'h-[calc(100vh-40px)]',
   className,
 )}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/ui/tailwind/src/components/dialog/Dialog.tsx` around lines 72 - 95,
Update the scrollable styling in Content so dialog sizes with the screen/hero
full-viewport contract do not receive dialogScrollableStyle’s
h-[calc(100vh-40px)] override. Apply the scrollable height rule only to
applicable sizes while preserving overflow behavior and existing class merging
for other dialog sizes.
🧹 Nitpick comments (2)
apps/web/src/app/[locale]/game/quiz/solve/_components/solving/SolvingQuizSection.tsx (1)

6-6: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

@gitanimals/ui-panda로 UI 컴포넌트 import를 통일하세요.

  • apps/web/src/app/[locale]/game/quiz/solve/_components/solving/SolvingQuizSection.tsx
  • apps/web/src/app/[locale]/mypage/my-pet/(evolution)/EvolutionPersona.tsx
  • apps/web/src/app/[locale]/mypage/my-pet/(merge)/MergePersona.tsx

ButtonDialog@gitanimals/ui-tailwind 대신 @gitanimals/ui-panda에서 가져오면 됩니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/web/src/app/`[locale]/game/quiz/solve/_components/solving/SolvingQuizSection.tsx
at line 6, Unify UI component imports by changing Button and Dialog imports from
`@gitanimals/ui-tailwind` to `@gitanimals/ui-panda` in SolvingQuizSection.tsx (line
6), EvolutionPersona.tsx (line 7), and MergePersona.tsx (line 7); leave other
imports unchanged.

Source: Coding guidelines

apps/web/src/app/[locale]/game/quiz/_components/CreateOrSolve/SelectQuizType.tsx (1)

34-37: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

onConfirm 내 불필요한 close() 호출 제거

신규 도입된 Dialog.Confirm 컴포넌트는 내부적으로 onConfirm 비동기/동기 작업이 완료된 직후 자동으로 onOpenChange(false)를 호출하여 다이얼로그를 닫습니다. 따라서 onConfirm 콜백 내에서 수동으로 close()를 호출할 필요가 없으며, 중복된 상태 업데이트를 방지하기 위해 제거를 권장합니다.

  • apps/web/src/app/[locale]/game/quiz/_components/CreateOrSolve/SelectQuizType.tsx#L34-L37: onConfirm 내의 close() 호출을 제거하고 onConfirm={handleCheckLanguage} 형태로 간소화하세요.
  • apps/web/src/app/[locale]/game/quiz/_components/CreateOrSolve/SelectQuizType.tsx#L51-L54: onConfirm 내의 close() 호출을 제거하고 onConfirm={() => router.push(ROUTE.GAME.QUIZ.SOLVE())} 형태로 수정하세요.
  • apps/web/src/app/[locale]/game/quiz/create/_components/QuizCreateForm.tsx#L53-L56: onConfirm 내의 close() 호출을 제거하고 onConfirm={toggleLocale} 형태로 간소화하세요.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/web/src/app/`[locale]/game/quiz/_components/CreateOrSolve/SelectQuizType.tsx
around lines 34 - 37, Remove the redundant close() calls from all Dialog.Confirm
onConfirm handlers: in
apps/web/src/app/[locale]/game/quiz/_components/CreateOrSolve/SelectQuizType.tsx
lines 34-37, replace the wrapper with onConfirm={handleCheckLanguage}; at lines
51-54, use onConfirm={() => router.push(ROUTE.GAME.QUIZ.SOLVE())}; and in
apps/web/src/app/[locale]/game/quiz/create/_components/QuizCreateForm.tsx lines
53-56, use onConfirm={toggleLocale}.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@apps/web/src/app/`[locale]/game/quiz/_components/CreateOrSolve/SelectQuizType.tsx:
- Line 4: Update imports in SelectQuizType.tsx (line 4), QuizCreateForm.tsx
(line 6), and useDialog.tsx (line 5) so Dialog and Button are sourced from
`@gitanimals/ui-panda` instead of `@gitanimals/ui-tailwind`; no other behavior
changes are needed.

In `@apps/web/src/app/`[locale]/shop/_petGotcha/GachaHatchGame.tsx:
- Around line 166-169: Update the failure branch after `await onDraw()` in
`GachaHatchGame` to stop calling `onClose()` when `res` is undefined, since
`OnePet.onDraw` already handles the error and closure; retain only the early
return.

In `@apps/web/src/components/Global/LoginCallbackWatcher.tsx`:
- Around line 3-31: Wrap the useSearchParams-dependent logic in
LoginCallbackWatcher with a React Suspense boundary, separating the existing
watcher implementation from the wrapper component as needed. Preserve the
callbackUrl capture, validation, storage, and URL cleanup behavior while
ensuring the globally inserted utility provides its own nearest Suspense
boundary.

In `@apps/web/src/components/Global/SessionExpiredDialog.tsx`:
- Line 4: Update the Button and Dialog import in SessionExpiredDialog to use the
`@gitanimals/ui-panda` package instead of `@gitanimals/ui-tailwind`, leaving the
component usage unchanged.

---

Outside diff comments:
In `@apps/web/src/app/`[locale]/shop/_petGotcha/TenPet.tsx:
- Around line 61-68: Update the error notification in the pet-draw failure
handler around sendMessageToErrorChannel to remove data?.user.accessToken
entirely and avoid serializing the full error object; include only explicitly
allowed, non-sensitive error fields in the Slack message.

In `@packages/ui/tailwind/src/components/dialog/Dialog.tsx`:
- Around line 72-95: Update the scrollable styling in Content so dialog sizes
with the screen/hero full-viewport contract do not receive
dialogScrollableStyle’s h-[calc(100vh-40px)] override. Apply the scrollable
height rule only to applicable sizes while preserving overflow behavior and
existing class merging for other dialog sizes.

---

Nitpick comments:
In
`@apps/web/src/app/`[locale]/game/quiz/_components/CreateOrSolve/SelectQuizType.tsx:
- Around line 34-37: Remove the redundant close() calls from all Dialog.Confirm
onConfirm handlers: in
apps/web/src/app/[locale]/game/quiz/_components/CreateOrSolve/SelectQuizType.tsx
lines 34-37, replace the wrapper with onConfirm={handleCheckLanguage}; at lines
51-54, use onConfirm={() => router.push(ROUTE.GAME.QUIZ.SOLVE())}; and in
apps/web/src/app/[locale]/game/quiz/create/_components/QuizCreateForm.tsx lines
53-56, use onConfirm={toggleLocale}.

In
`@apps/web/src/app/`[locale]/game/quiz/solve/_components/solving/SolvingQuizSection.tsx:
- Line 6: Unify UI component imports by changing Button and Dialog imports from
`@gitanimals/ui-tailwind` to `@gitanimals/ui-panda` in SolvingQuizSection.tsx (line
6), EvolutionPersona.tsx (line 7), and MergePersona.tsx (line 7); leave other
imports unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: ff57aec2-b84b-4a36-b6d0-9f74758f5b90

📥 Commits

Reviewing files that changed from the base of the PR and between b4b1771 and 4fe170e.

⛔ Files ignored due to path filters (2)
  • apps/web/public/shop/egg-hatch.png is excluded by !**/*.png
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (44)
  • apps/web/messages/en-US.json
  • apps/web/messages/ko-KR.json
  • apps/web/package.json
  • apps/web/src/apis/interceptor.ts
  • apps/web/src/app/[locale]/auth/desktop/page.tsx
  • apps/web/src/app/[locale]/auth/error/page.tsx
  • apps/web/src/app/[locale]/game/quiz/_components/CreateOrSolve/SelectQuizType.tsx
  • apps/web/src/app/[locale]/game/quiz/create/_components/QuizCreateForm.tsx
  • apps/web/src/app/[locale]/game/quiz/solve/_components/done/CompleteAlertDialog.tsx
  • apps/web/src/app/[locale]/game/quiz/solve/_components/fail/FailAlertDialog.tsx
  • apps/web/src/app/[locale]/game/quiz/solve/_components/solving/SolvingQuizSection.tsx
  • apps/web/src/app/[locale]/game/quiz/solve/_components/success/CorrectConfirmDialog.tsx
  • apps/web/src/app/[locale]/laboratory/_components/AlertDialog.tsx
  • apps/web/src/app/[locale]/mypage/(github-custom)/FarmPersonaSelect.tsx
  • apps/web/src/app/[locale]/mypage/(github-custom)/LinePersonaSelect.tsx
  • apps/web/src/app/[locale]/mypage/my-pet/(evolution)/EvolutionPersona.tsx
  • apps/web/src/app/[locale]/mypage/my-pet/(merge)/MergePersona.tsx
  • apps/web/src/app/[locale]/mypage/my-pet/SelectedPetTable.tsx
  • apps/web/src/app/[locale]/shop/_auction/PersonaSearch.tsx
  • apps/web/src/app/[locale]/shop/_auction/SellSection/EditModal.tsx
  • apps/web/src/app/[locale]/shop/_petGotcha/CardPackGame.tsx
  • apps/web/src/app/[locale]/shop/_petGotcha/GachaHatchGame.tsx
  • apps/web/src/app/[locale]/shop/_petGotcha/OnePet.tsx
  • apps/web/src/app/[locale]/shop/_petGotcha/TenCardFlipGame.tsx
  • apps/web/src/app/[locale]/shop/_petGotcha/TenPet.tsx
  • apps/web/src/components/AuthButton.tsx
  • apps/web/src/components/CardGame/FanDrawingGame/CardMotion.tsx
  • apps/web/src/components/CardGame/FanDrawingGame/FanDrawingGame.tsx
  • apps/web/src/components/ConfirmDialog/ConfirmDialog.tsx
  • apps/web/src/components/ConfirmDialog/index.ts
  • apps/web/src/components/Global/GlobalComponent.tsx
  • apps/web/src/components/Global/LoginCallbackWatcher.tsx
  • apps/web/src/components/Global/SessionExpiredDialog.tsx
  • apps/web/src/components/Global/useDialog.tsx
  • apps/web/src/components/RouteModal.tsx
  • apps/web/src/middleware.ts
  • apps/web/src/utils/sessionExpired.ts
  • package.json
  • packages/ui/tailwind/src/components/dialog/Alert.tsx
  • packages/ui/tailwind/src/components/dialog/CommonDialog.tsx
  • packages/ui/tailwind/src/components/dialog/Confirm.tsx
  • packages/ui/tailwind/src/components/dialog/Dialog.stories.tsx
  • packages/ui/tailwind/src/components/dialog/Dialog.tsx
  • packages/ui/tailwind/src/components/dialog/index.ts
💤 Files with no reviewable changes (10)
  • apps/web/src/app/[locale]/game/quiz/solve/_components/success/CorrectConfirmDialog.tsx
  • packages/ui/tailwind/src/components/dialog/CommonDialog.tsx
  • apps/web/src/app/[locale]/shop/_petGotcha/TenCardFlipGame.tsx
  • apps/web/src/app/[locale]/game/quiz/solve/_components/done/CompleteAlertDialog.tsx
  • apps/web/src/app/[locale]/game/quiz/solve/_components/fail/FailAlertDialog.tsx
  • apps/web/src/components/ConfirmDialog/ConfirmDialog.tsx
  • apps/web/src/components/CardGame/FanDrawingGame/CardMotion.tsx
  • apps/web/src/components/CardGame/FanDrawingGame/FanDrawingGame.tsx
  • apps/web/src/app/[locale]/laboratory/_components/AlertDialog.tsx
  • apps/web/src/components/ConfirmDialog/index.ts

'use client';

import { useTranslations } from 'next-intl';
import { Dialog } from '@gitanimals/ui-tailwind';

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# ui-panda 패키지 내 Dialog 및 Button 컴포넌트 존재 여부 확인
fd -i -t f "dialog|button" packages/ui/panda

Repository: git-goods/git-animal-client

Length of output: 440


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the relevant files and the ui-panda package entrypoints/exports.
printf '\n== file list ==\n'
git ls-files \
  'apps/web/src/app/[locale]/game/quiz/_components/CreateOrSolve/SelectQuizType.tsx' \
  'apps/web/src/app/[locale]/game/quiz/create/_components/QuizCreateForm.tsx' \
  'apps/web/src/components/Global/useDialog.tsx' \
  'packages/ui/panda/src/components/Dialog/Dialog.tsx' \
  'packages/ui/panda/src/components/Button/Button.tsx' \
  'packages/ui/panda/src/index.ts' \
  'packages/ui/panda/src/components/index.ts' \
  'packages/ui/panda/package.json' || true

printf '\n== package.json ==\n'
sed -n '1,220p' packages/ui/panda/package.json

printf '\n== index files ==\n'
for f in packages/ui/panda/src/index.ts packages/ui/panda/src/components/index.ts; do
  if [ -f "$f" ]; then
    echo "--- $f ---"
    sed -n '1,220p' "$f"
  fi
done

printf '\n== Dialog/Button implementations (heads) ==\n'
for f in packages/ui/panda/src/components/Dialog/Dialog.tsx packages/ui/panda/src/components/Button/Button.tsx; do
  echo "--- $f ---"
  sed -n '1,220p' "$f"
done

printf '\n== relevant app files (selected ranges) ==\n'
for f in \
  apps/web/src/app/[locale]/game/quiz/_components/CreateOrSolve/SelectQuizType.tsx \
  apps/web/src/app/[locale]/game/quiz/create/_components/QuizCreateForm.tsx \
  apps/web/src/components/Global/useDialog.tsx
do
  echo "--- $f ---"
  nl -ba "$f" | sed -n '1,220p'
done

Repository: git-goods/git-animal-client

Length of output: 7342


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Map the ui-panda component export surface to see whether Dialog/Button are re-exported.
for f in packages/ui/panda/src/index.ts packages/ui/panda/src/components/index.ts; do
  [ -f "$f" ] || continue
  echo "--- outline $f ---"
  ast-grep outline "$f" --view expanded || true
done

Repository: git-goods/git-animal-client

Length of output: 950


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check whether the app code imports from ui-tailwind and whether ui-panda is already used nearby.
rg -n "`@gitanimals/ui-tailwind`|`@gitanimals/ui-panda`" \
  apps/web/src/app/[locale]/game/quiz/_components/CreateOrSolve/SelectQuizType.tsx \
  apps/web/src/app/[locale]/game/quiz/create/_components/QuizCreateForm.tsx \
  apps/web/src/components/Global/useDialog.tsx

Repository: git-goods/git-animal-client

Length of output: 529


UI 컴포넌트 임포트를 @gitanimals/ui-panda로 바꾸세요

  • apps/web/src/app/[locale]/game/quiz/_components/CreateOrSolve/SelectQuizType.tsx
  • apps/web/src/app/[locale]/game/quiz/create/_components/QuizCreateForm.tsx
  • apps/web/src/components/Global/useDialog.tsx

DialogButton@gitanimals/ui-tailwind가 아니라 @gitanimals/ui-panda에서 가져오도록 맞추세요.

📍 Affects 3 files
  • apps/web/src/app/[locale]/game/quiz/_components/CreateOrSolve/SelectQuizType.tsx#L4-L4 (this comment)
  • apps/web/src/app/[locale]/game/quiz/create/_components/QuizCreateForm.tsx#L6-L6
  • apps/web/src/components/Global/useDialog.tsx#L5-L5
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/web/src/app/`[locale]/game/quiz/_components/CreateOrSolve/SelectQuizType.tsx
at line 4, Update imports in SelectQuizType.tsx (line 4), QuizCreateForm.tsx
(line 6), and useDialog.tsx (line 5) so Dialog and Button are sourced from
`@gitanimals/ui-panda` instead of `@gitanimals/ui-tailwind`; no other behavior
changes are needed.

Source: Coding guidelines

Comment on lines +166 to +169
const res = await onDraw();
if (!res) {
onClose(); // onDraw가 에러 토스트/닫기를 처리함
return;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

실패 시 onClose()가 두 번 호출됩니다.

OnePet.onDraw가 이미 오류 처리와 onClose()를 수행한 뒤 undefined를 반환하므로, 여기서 다시 닫으면 router.back()이 두 번 실행되어 이전 페이지까지 이탈할 수 있습니다.

🐛 제안 수정
 const res = await onDraw();
 if (!res) {
-  onClose(); // onDraw가 에러 토스트/닫기를 처리함
   return;
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const res = await onDraw();
if (!res) {
onClose(); // onDraw가 에러 토스트/닫기를 처리함
return;
const res = await onDraw();
if (!res) {
return;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/src/app/`[locale]/shop/_petGotcha/GachaHatchGame.tsx around lines
166 - 169, Update the failure branch after `await onDraw()` in `GachaHatchGame`
to stop calling `onClose()` when `res` is undefined, since `OnePet.onDraw`
already handles the error and closure; retain only the early return.

Comment on lines +3 to +31
import { useEffect } from 'react';
import { useSearchParams } from 'next/navigation';

import { LOCAL_STORAGE_KEY } from '@/constants/storage';

// 보호 라우트에서 미들웨어가 홈으로 되돌릴 때 실어 보낸 `?callbackUrl` 을 캡처해
// localStorage 에 저장한다. 사용자가 로그인하면 login()/LoginButton 이 이 값을 읽어
// 원래 목적지로 자동 복귀시킨다. (useSearchParams 구독으로 클라 네비게이션 유입도 감지)
export function LoginCallbackWatcher() {
const searchParams = useSearchParams();

useEffect(() => {
const callbackUrl = searchParams.get('callbackUrl');
if (!callbackUrl) return;

// open-redirect 방지: 외부 절대 URL(`//`, `https://…`)은 무시하고 앱 내부 경로만 허용.
const isInternalPath = callbackUrl.startsWith('/') && !callbackUrl.startsWith('//');
if (isInternalPath) {
localStorage.setItem(LOCAL_STORAGE_KEY.callbackUrl, callbackUrl);
}

const params = new URLSearchParams(searchParams.toString());
params.delete('callbackUrl');
const query = params.toString();
window.history.replaceState(null, '', window.location.pathname + (query ? `?${query}` : ''));
}, [searchParams]);

return null;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚀 Performance & Scalability | 🟠 Major | ⚡ Quick win

useSearchParams 사용 컴포넌트에 Suspense 바운더리 적용

Next.js 환경에서 클라이언트 컴포넌트가 useSearchParams를 사용할 때 가장 가까운 상위 Suspense 바운더리가 없으면, 빌드 시 해당 라우트가 정적 생성에서 제외되거나 강제 동적 렌더링(De-opt)이 발생할 수 있습니다. 전역으로 삽입되는 유틸리티 컴포넌트이므로 내부에서 Suspense로 감싸는 것을 권장합니다.

🛠️ 수정 제안
-import { useEffect } from 'react';
+import { useEffect, Suspense } from 'react';
 import { useSearchParams } from 'next/navigation';
 
 import { LOCAL_STORAGE_KEY } from '`@/constants/storage`';
 
 // 보호 라우트에서 미들웨어가 홈으로 되돌릴 때 실어 보낸 `?callbackUrl` 을 캡처해
 // localStorage 에 저장한다. 사용자가 로그인하면 login()/LoginButton 이 이 값을 읽어
 // 원래 목적지로 자동 복귀시킨다. (useSearchParams 구독으로 클라 네비게이션 유입도 감지)
-export function LoginCallbackWatcher() {
+function LoginCallbackWatcherContent() {
   const searchParams = useSearchParams();
 
   useEffect(() => {
     const callbackUrl = searchParams.get('callbackUrl');
     if (!callbackUrl) return;
 
     // open-redirect 방지: 외부 절대 URL(`//`, `https://…`)은 무시하고 앱 내부 경로만 허용.
     const isInternalPath = callbackUrl.startsWith('/') && !callbackUrl.startsWith('//');
     if (isInternalPath) {
       localStorage.setItem(LOCAL_STORAGE_KEY.callbackUrl, callbackUrl);
     }
 
     const params = new URLSearchParams(searchParams.toString());
     params.delete('callbackUrl');
     const query = params.toString();
     window.history.replaceState(null, '', window.location.pathname + (query ? `?${query}` : ''));
   }, [searchParams]);
 
   return null;
 }
+
+export function LoginCallbackWatcher() {
+  return (
+    <Suspense fallback={null}>
+      <LoginCallbackWatcherContent />
+    </Suspense>
+  );
+}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import { useEffect } from 'react';
import { useSearchParams } from 'next/navigation';
import { LOCAL_STORAGE_KEY } from '@/constants/storage';
// 보호 라우트에서 미들웨어가 홈으로 되돌릴 때 실어 보낸 `?callbackUrl` 을 캡처해
// localStorage 에 저장한다. 사용자가 로그인하면 login()/LoginButton 이 이 값을 읽어
// 원래 목적지로 자동 복귀시킨다. (useSearchParams 구독으로 클라 네비게이션 유입도 감지)
export function LoginCallbackWatcher() {
const searchParams = useSearchParams();
useEffect(() => {
const callbackUrl = searchParams.get('callbackUrl');
if (!callbackUrl) return;
// open-redirect 방지: 외부 절대 URL(`//`, `https://…`)은 무시하고 앱 내부 경로만 허용.
const isInternalPath = callbackUrl.startsWith('/') && !callbackUrl.startsWith('//');
if (isInternalPath) {
localStorage.setItem(LOCAL_STORAGE_KEY.callbackUrl, callbackUrl);
}
const params = new URLSearchParams(searchParams.toString());
params.delete('callbackUrl');
const query = params.toString();
window.history.replaceState(null, '', window.location.pathname + (query ? `?${query}` : ''));
}, [searchParams]);
return null;
}
import { useEffect, Suspense } from 'react';
import { useSearchParams } from 'next/navigation';
import { LOCAL_STORAGE_KEY } from '`@/constants/storage`';
// 보호 라우트에서 미들웨어가 홈으로 되돌릴 때 실어 보낸 `?callbackUrl` 을 캡처해
// localStorage 에 저장한다. 사용자가 로그인하면 login()/LoginButton 이 이 값을 읽어
// 원래 목적지로 자동 복귀시킨다. (useSearchParams 구독으로 클라 네비게이션 유입도 감지)
function LoginCallbackWatcherContent() {
const searchParams = useSearchParams();
useEffect(() => {
const callbackUrl = searchParams.get('callbackUrl');
if (!callbackUrl) return;
// open-redirect 방지: 외부 절대 URL(`//`, `https://…`)은 무시하고 앱 내부 경로만 허용.
const isInternalPath = callbackUrl.startsWith('/') && !callbackUrl.startsWith('//');
if (isInternalPath) {
localStorage.setItem(LOCAL_STORAGE_KEY.callbackUrl, callbackUrl);
}
const params = new URLSearchParams(searchParams.toString());
params.delete('callbackUrl');
const query = params.toString();
window.history.replaceState(null, '', window.location.pathname + (query ? `?${query}` : ''));
}, [searchParams]);
return null;
}
export function LoginCallbackWatcher() {
return (
<Suspense fallback={null}>
<LoginCallbackWatcherContent />
</Suspense>
);
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/src/components/Global/LoginCallbackWatcher.tsx` around lines 3 - 31,
Wrap the useSearchParams-dependent logic in LoginCallbackWatcher with a React
Suspense boundary, separating the existing watcher implementation from the
wrapper component as needed. Preserve the callbackUrl capture, validation,
storage, and URL cleanup behavior while ensuring the globally inserted utility
provides its own nearest Suspense boundary.

'use client';

import { useTranslations } from 'next-intl';
import { Button, Dialog } from '@gitanimals/ui-tailwind';

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

코딩 가이드라인 준수: UI 컴포넌트 임포트 경로 변경

코딩 가이드라인에 따라 UI 컴포넌트는 @gitanimals/ui-tailwind가 아닌 @gitanimals/ui-panda 패키지에서 가져와야 합니다.

♻️ 수정 제안
-import { Button, Dialog } from '`@gitanimals/ui-tailwind`';
+import { Button, Dialog } from '`@gitanimals/ui-panda`';
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import { Button, Dialog } from '@gitanimals/ui-tailwind';
import { Button, Dialog } from '`@gitanimals/ui-panda`';
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/src/components/Global/SessionExpiredDialog.tsx` at line 4, Update
the Button and Dialog import in SessionExpiredDialog to use the
`@gitanimals/ui-panda` package instead of `@gitanimals/ui-tailwind`, leaving the
component usage unchanged.

Source: Coding guidelines

@sumi-0011
sumi-0011 merged commit c18b228 into main Jul 23, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant